﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using Microsoft.Practices.Prism.Events;
using Hockey.Shared;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace Hockey
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private IEventAggregator _events;
        private int _inputBlockCounter = 0;

        private double _rationMax = 16d / 9d;
        private double _ratioMin = 4d / 3d;

        public MainWindow()
        {
            InitializeComponent();

            if (events == null)
                throw new ArgumentNullException("Event Aggregator");
            _events = events;

            // register events
            _events.GetEvent<SetInputBlocking>().Subscribe((blocked) =>
                {
                    if (blocked)
                        _inputBlockCounter++;
                    else
                        _inputBlockCounter--;
                }, true);

            _events.GetEvent<ExitApplication>().Subscribe((obj) =>
                {
                    Close();
                }, true);

            _events.GetEvent<StartProgress>().Subscribe((obj) =>
                {
                    StartProgress();
                }, true);

            _events.GetEvent<TerminateProgress>().Subscribe((obj) =>
                {
                    TerminateProgress();
                }, true);


            // add keyboard handlers
            AddHandler(Window.PreviewKeyUpEvent, new KeyEventHandler(PreviewKeyUpHandler));
            AddHandler(Window.PreviewKeyDownEvent, new KeyEventHandler(KeyDownHandler));

            // detect smaller displays - adjust the window size
            if (SystemParameters.PrimaryScreenWidth <= 1024d)
            {
                this.Width = 800;
                this.Height = 600;
            }
        }

        /// <summary>
        /// Starts thinking progress
        /// </summary>
        private void StartProgress()        
        {
            uxStateName.Text = "Přemýšlím...";
            uxProgress.IsIndeterminate = true;
        }

        /// <summary>
        /// Terminates thiking progress
        /// </summary>
        private void TerminateProgress()
        {
            uxStateName.Text = "Připraven";
            uxProgress.IsIndeterminate = false;
        }

        /// <summary>
        /// Handler for simulating the compass button pressing
        /// </summary>
        private void KeyDownHandler(object sender, KeyEventArgs args)
        {
            // check input blocked
            if (_inputBlockCounter > 0)
                return;

            // map direction by key
            eMoveDirection? dir = MapDirectionByKey(args);

            // if not valid key, exit
            if (dir.HasValue == false)
                return;

            // notify to press the compass button
            _events.GetEvent<PressCompassButton>().Publish(dir.Value);

            args.Handled = true;
        }

        /// <summary>
        /// Handler firing the move step 
        /// </summary>
        private void PreviewKeyUpHandler(object sender, KeyEventArgs args)
        {
            // check input blocked
            if (_inputBlockCounter > 0)
                return;

            // map direction by key
            eMoveDirection? dir = MapDirectionByKey(args);

            // if not valid key, exit
            if (dir.HasValue == false)
                return;

            // notify to unpress the compass button
            _events.GetEvent<UnpressCompassButton>().Publish(dir.Value);
            
            // notify the move step           
            _events.GetEvent<MoveStepFired>().Publish(dir.Value);

            args.Handled = true;
        }


        /// <summary>
        /// Maps the direction by associated key
        /// </summary>
        private eMoveDirection? MapDirectionByKey(KeyEventArgs args)
        {
            switch (args.Key)
            {
                case Key.NumPad1:
                case Key.End:
                    return eMoveDirection.SouthWest;
                case Key.NumPad2:
                case Key.Down:
                    return eMoveDirection.South;
                case Key.NumPad3:
                case Key.PageDown:
                    return eMoveDirection.SouthEast;
                case Key.NumPad4:
                case Key.Left:
                    return eMoveDirection.West;
                case Key.NumPad6:
                case Key.Right:
                    return eMoveDirection.East;
                case Key.NumPad7:
                case Key.Home:
                    return eMoveDirection.NorthWest;
                case Key.NumPad8:
                case Key.Up:
                    return eMoveDirection.North;
                case Key.NumPad9:
                case Key.PageUp:
                    return eMoveDirection.NorthEast;
                default:
                    return null;
            } // switch      
        }

        /// <summary>
        /// Question, whether to close the app
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            e.Cancel = false;

            // run the message box
            if (MessageBox.Show("Opravdu chcete ukončit tento program?", "Ukončení aplikace",
                MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
            {
                e.Cancel = true;
            }          
        }

        #region Aspect Ratio Resizing

        protected override void OnSourceInitialized(EventArgs e)
        {
            ((HwndSource)HwndSource.FromVisual(this)).AddHook(DragHook);

            base.OnSourceInitialized(e);
        }

        internal enum WM
        {
            WINDOWPOSCHANGING = 0x0046,
        }

        [Flags()]
        public enum SWP
        {
            NoMove = 0x2,
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct WINDOWPOS
        {
            public IntPtr hwnd;
            public IntPtr hwndInsertAfter;
            public int x;
            public int y;
            public int cx;
            public int cy;
            public int flags;
        }

        private IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if ((WM)msg == WM.WINDOWPOSCHANGING)
            {
                WINDOWPOS position = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));

                if ((position.flags & (int)SWP.NoMove) != 0 ||
                    HwndSource.FromHwnd(hwnd).RootVisual == null) return IntPtr.Zero;

                // compute dynamic aspect ration
                int max_cx = (int)(position.cy * _rationMax);
                int min_cx = (int)(position.cy * _ratioMin);
                if(position.cx > max_cx)
                    position.cx = max_cx;
                if(position.cx < min_cx)
                    position.cx = min_cx;

                Marshal.StructureToPtr(position, lParam, true);
                handled = true;
            }

            return IntPtr.Zero;
        }

        #endregion

    }
}
